package com.allenkerr.lucene;

import org.apache.lucene.analysis.Analyzer;
import org.apache.lucene.document.Document;
import org.apache.lucene.document.LongPoint;
import org.apache.lucene.index.DirectoryReader;
import org.apache.lucene.index.IndexReader;
import org.apache.lucene.index.Term;
import org.apache.lucene.queryparser.classic.MultiFieldQueryParser;
import org.apache.lucene.queryparser.classic.ParseException;
import org.apache.lucene.queryparser.classic.QueryParser;
import org.apache.lucene.search.*;
import org.apache.lucene.store.FSDirectory;
import org.lionsoul.jcseg.analyzer.JcsegAnalyzer;
import org.lionsoul.jcseg.tokenizer.core.JcsegTaskConfig;

import java.io.IOException;
import java.io.InputStream;
import java.nio.file.Paths;
import java.util.Properties;

/**
 * Created by 小柯(Allen Kerr) on 17.4.29 14:46.
 */
public class IndexSearch {
    //索引文件所在目录
    private static String searchIndexDirPath;

    //使用静态代码块加载配置文件，初始化属性
    static {
        InputStream inputStream = IndexManager.class.getClassLoader().getResourceAsStream("config.properties");
        Properties properties = new Properties();
        try {
            properties.load(inputStream);
            inputStream.close();
        } catch (IOException e) {
            System.out.println("读取配置文件失败！");
            e.printStackTrace();
        }
        searchIndexDirPath = properties.getProperty("search_index_dir");
    }

    /**
     * QueryParser：根据域名进行搜索，可以设置默认搜索域（只能在文本内容中搜索）
     * 域名:搜索关键词
     */
    public static void indexSearch_QueryParser(String fieldAndKeyword) throws ParseException, IOException {
        //1. 创建分词器，必须与索引创建时使用的分词器一致
//        StandardAnalyzer analyzer = new StandardAnalyzer();
        //lucene 4.x版本
        //Analyzer analyzer = new JcsegAnalyzer4X(JcsegTaskConfig.COMPLEX_MODE);
        //lucene 6.3.0以及以上版本
        Analyzer analyzer = new JcsegAnalyzer(JcsegTaskConfig.COMPLEX_MODE);

        //2. 创建查询对象，构造函数(默认搜索域, 分词器)，默认搜索域：如果搜索语法中没有指定搜索域，就使用默认搜索域
        QueryParser queryParser = new QueryParser("fileContext", analyzer);
        //3. 创建查询语句对象，构造函数("域名:搜索关键词")
        Query query = queryParser.parse(fieldAndKeyword);
        //4. 指定索引和文档的目录
        FSDirectory directory = FSDirectory.open(Paths.get(searchIndexDirPath));
        /**
         * lucene 4 中，IndexReader.open(directory)过时，但未被移除
         * lucene 5 中，IndexReader.open(directory)已被移除。
         * 参考链接：http://codepub.cn/2016/05/24/Lucene-6-0-in-action-5-The-index-search-an-IndexSearcher/
         */
        //5. 创建索引和文档的读取对象
        IndexReader indexReader = DirectoryReader.open(directory);
        //6. 创建索引的搜索对象
        IndexSearcher indexSearcher = new IndexSearcher(indexReader);
        //7. 执行搜索，参数：查询语句对象，查多少条记录
        TopDocs topDocs = indexSearcher.search(query, 6);

        //打印结果
        printSearchResult(indexReader, topDocs);
    }

    /**
     * TermQuery：根据词进行搜索（只能在文本内容中搜索）
     */
    public static void indexSearch_TermQuery(String field, String Keyword) throws ParseException, IOException {
        //1. 创建分词器，必须与索引创建时使用的分词器一致
//        StandardAnalyzer analyzer = new StandardAnalyzer();
        //lucene 4.x版本
        //Analyzer analyzer = new JcsegAnalyzer4X(JcsegTaskConfig.COMPLEX_MODE);
        //lucene 6.3.0以及以上版本
        Analyzer analyzer = new JcsegAnalyzer(JcsegTaskConfig.COMPLEX_MODE);

        //2. 创建词元 Term ————> 就是词，然后创建查询语句对象
        Term term = new Term(field, Keyword);
        Query query = new TermQuery(term);

        //3. 指定索引和文档的目录
        FSDirectory directory = FSDirectory.open(Paths.get(searchIndexDirPath));
        /**
         * lucene 4 中，IndexReader.open(directory)过时，但未被移除
         * lucene 5 中，IndexReader.open(directory)已被移除。
         * 参考链接：http://codepub.cn/2016/05/24/Lucene-6-0-in-action-5-The-index-search-an-IndexSearcher/
         */
        //4. 创建索引和文档的读取对象
        IndexReader indexReader = DirectoryReader.open(directory);
        //5. 创建索引的搜索对象
        IndexSearcher indexSearcher = new IndexSearcher(indexReader);
        //6. 执行搜索，参数：查询语句对象，查多少条记录
        TopDocs topDocs = indexSearcher.search(query, 6);

        //打印结果
        printSearchResult(indexReader, topDocs);
    }

    /**
     * LegacyNumericRangeQuery：根据数值范围进行搜索
     * 类已过时！
     */
    public static void indexSearch_LegacyNumericRangeQuery(long min, long max) throws ParseException, IOException {
        //1. 创建分词器，必须与索引创建时使用的分词器一致
//        StandardAnalyzer analyzer = new StandardAnalyzer();
        //lucene 4.x版本
        //Analyzer analyzer = new JcsegAnalyzer4X(JcsegTaskConfig.COMPLEX_MODE);
        //lucene 6.3.0以及以上版本
        Analyzer analyzer = new JcsegAnalyzer(JcsegTaskConfig.COMPLEX_MODE);

        //2. 创建数值范围查询对象
        Query query = LegacyNumericRangeQuery.newLongRange("fileSize", min, max, true, true);

        //3. 指定索引和文档的目录
        FSDirectory directory = FSDirectory.open(Paths.get(searchIndexDirPath));
        /**
         * lucene 4 中，IndexReader.open(directory)过时，但未被移除
         * lucene 5 中，IndexReader.open(directory)已被移除。
         * 参考链接：http://codepub.cn/2016/05/24/Lucene-6-0-in-action-5-The-index-search-an-IndexSearcher/
         */
        //4. 创建索引和文档的读取对象
        IndexReader indexReader = DirectoryReader.open(directory);
        //5. 创建索引的搜索对象
        IndexSearcher indexSearcher = new IndexSearcher(indexReader);
        //6. 执行搜索，参数：查询语句对象，查多少条记录
        TopDocs topDocs = indexSearcher.search(query, 6);

        //打印结果
        printSearchResult(indexReader, topDocs);
    }

    /**
     * LongPoint.newRangeQuery：根据数值范围进行搜索
     * 在索引创建时，使用LongPoint存储fileSize属性，才能使用该方法搜索
     */
    public static void indexSearch_newRangeQuery(long min, long max) throws ParseException, IOException {
        //1. 创建分词器，必须与索引创建时使用的分词器一致
        //lucene 4.x版本
        //Analyzer analyzer = new JcsegAnalyzer4X(JcsegTaskConfig.COMPLEX_MODE);
        //lucene 6.3.0以及以上版本
        Analyzer analyzer = new JcsegAnalyzer(JcsegTaskConfig.COMPLEX_MODE);

        //2. 创建数值范围查询对象
        Query query = LongPoint.newRangeQuery("fileSize", min, max);

        //3. 指定索引和文档的目录
        FSDirectory directory = FSDirectory.open(Paths.get(searchIndexDirPath));
        /**
         * lucene 4 中，IndexReader.open(directory)过时，但未被移除
         * lucene 5 中，IndexReader.open(directory)已被移除。
         * 参考链接：http://codepub.cn/2016/05/24/Lucene-6-0-in-action-5-The-index-search-an-IndexSearcher/
         */
        //4. 创建索引和文档的读取对象
        IndexReader indexReader = DirectoryReader.open(directory);
        //5. 创建索引的搜索对象
        IndexSearcher indexSearcher = new IndexSearcher(indexReader);
        //6. 执行搜索，参数：查询语句对象，查多少条记录
        TopDocs topDocs = indexSearcher.search(query, 6);

        //打印结果
        printSearchResult(indexReader, topDocs);
    }

    /**
     * BooleanQuery：组合查询
     */
    public static void indexSearch_BooleanQuery() throws ParseException, IOException {
        //1. 创建分词器，必须与索引创建时使用的分词器一致
        //lucene 4.x版本
        //Analyzer analyzer = new JcsegAnalyzer4X(JcsegTaskConfig.COMPLEX_MODE);
        //lucene 6.3.0以及以上版本
        Analyzer analyzer = new JcsegAnalyzer(JcsegTaskConfig.COMPLEX_MODE);

        //2. 创建布尔查询对象的Builder对象
        BooleanQuery.Builder builder = new BooleanQuery.Builder();
        //创建查询条件
        TermQuery query1 = new TermQuery(new Term("fileName", "传说"));
        TermQuery query2 = new TermQuery(new Term("fileContext", "许毅"));
        Query query3 = LongPoint.newRangeQuery("fileSize", 100L, 20000L);
        //添加查询条件
        builder.add(query1, BooleanClause.Occur.MUST);
        builder.add(query2, BooleanClause.Occur.MUST);
        builder.add(query3, BooleanClause.Occur.MUST);
        //创建布尔查询对象
        BooleanQuery booleanQuery = builder.build();

        //3. 指定索引和文档的目录
        FSDirectory directory = FSDirectory.open(Paths.get(searchIndexDirPath));
        /**
         * lucene 4 中，IndexReader.open(directory)过时，但未被移除
         * lucene 5 中，IndexReader.open(directory)已被移除。
         * 参考链接：http://codepub.cn/2016/05/24/Lucene-6-0-in-action-5-The-index-search-an-IndexSearcher/
         */
        //4. 创建索引和文档的读取对象
        IndexReader indexReader = DirectoryReader.open(directory);
        //5. 创建索引的搜索对象
        IndexSearcher indexSearcher = new IndexSearcher(indexReader);
        //6. 执行搜索，参数：查询语句对象，查多少条记录
        TopDocs topDocs = indexSearcher.search(booleanQuery, 6);

        //打印结果
        printSearchResult(indexReader, topDocs);
    }

    /**
     * MatchAllDocsQuery：查询索引目录下所有的文档
     */
    public static void indexSearch_MatchAllDocsQuery() throws ParseException, IOException {
        //1. 创建分词器，必须与索引创建时使用的分词器一致
        //lucene 4.x版本
        //Analyzer analyzer = new JcsegAnalyzer4X(JcsegTaskConfig.COMPLEX_MODE);
        //lucene 6.3.0以及以上版本
        Analyzer analyzer = new JcsegAnalyzer(JcsegTaskConfig.COMPLEX_MODE);

        //2. 创建查询对象
        Query query = new MatchAllDocsQuery();

        //3. 指定索引和文档的目录
        FSDirectory directory = FSDirectory.open(Paths.get(searchIndexDirPath));
        /**
         * lucene 4 中，IndexReader.open(directory)过时，但未被移除
         * lucene 5 中，IndexReader.open(directory)已被移除。
         * 参考链接：http://codepub.cn/2016/05/24/Lucene-6-0-in-action-5-The-index-search-an-IndexSearcher/
         */
        //4. 创建索引和文档的读取对象
        IndexReader indexReader = DirectoryReader.open(directory);
        //5. 创建索引的搜索对象
        IndexSearcher indexSearcher = new IndexSearcher(indexReader);
        //6. 执行搜索，参数：查询语句对象，查多少条记录
        TopDocs topDocs = indexSearcher.search(query, 6);

        //打印结果
        printSearchResult(indexReader, topDocs);
    }

    /**
     * MultiFieldQueryParser：指定多个搜索域
     */
    public static void indexSearch_MultiFieldQueryParser() throws ParseException, IOException {
        //1. 创建分词器，必须与索引创建时使用的分词器一致
        //lucene 4.x版本
        //Analyzer analyzer = new JcsegAnalyzer4X(JcsegTaskConfig.COMPLEX_MODE);
        //lucene 6.3.0以及以上版本
        Analyzer analyzer = new JcsegAnalyzer(JcsegTaskConfig.COMPLEX_MODE);

        //2.1 指定搜索域
        String[] fields = {"fileName", "fileContext"};
        //2.2 创建MultiFieldQueryParser对象
        MultiFieldQueryParser queryParser = new MultiFieldQueryParser(fields, analyzer);
        //2.3 创建查询语句对象
        Query query = queryParser.parse("金三胖");

        //3. 指定索引和文档的目录
        FSDirectory directory = FSDirectory.open(Paths.get(searchIndexDirPath));
        /**
         * lucene 4 中，IndexReader.open(directory)过时，但未被移除
         * lucene 5 中，IndexReader.open(directory)已被移除。
         * 参考链接：http://codepub.cn/2016/05/24/Lucene-6-0-in-action-5-The-index-search-an-IndexSearcher/
         */
        //4. 创建索引和文档的读取对象
        IndexReader indexReader = DirectoryReader.open(directory);
        //5. 创建索引的搜索对象
        IndexSearcher indexSearcher = new IndexSearcher(indexReader);
        //6. 执行搜索，参数：查询语句对象，查多少条记录
        TopDocs topDocs = indexSearcher.search(query, 6);

        //打印结果
        printSearchResult(indexReader, topDocs);
    }


    /**
     * 打印搜索结果
     *
     * @param indexReader
     * @param topDocs
     * @throws IOException
     */
    private static void printSearchResult(IndexReader indexReader, TopDocs topDocs) throws IOException {
        //1. 共找到多少条记录
        System.out.println("一共找到 " + topDocs.totalHits + " 条记录！");
        //2. 取结果集，并打印出来
        ScoreDoc[] scoreDocs = topDocs.scoreDocs;
        int i = 1;
        for (ScoreDoc scoreDoc : scoreDocs) {
            //获取 docId
            int docId = scoreDoc.doc;
            //通过 docId 读取硬盘上对应的文档
            Document doc = indexReader.document(docId);
            //get(域名) 取值
            System.out.println("--------------------------");
            System.out.println("第 " + i++ + " 条记录：");
            System.out.println("名字：" + doc.get("fileName"));
            System.out.println("大小：" + doc.get("fileSize"));
            if (doc.get("fileContext") != null) {
                System.out.println("内容：" + doc.get("fileContext").substring(0, 40) + "……");
            } else {
                System.out.println("文档内容未存储！");
            }
            System.out.println("--------------------------");
        }
    }


}
